# include "App.h"
# include "Client.h"
# include "MainWindow.h"
# include "ShowRemoteWindow.h"
# include "VideoCapture.h"
# include "AudioCapture.h"
# include "SaveFileFilter.h"
# include "GetDataFilter.h"
# include "VideoEncoder.h"
# include "VideoDecoder.h"
# include "AudioEncoder.h"
# include "samplefmt.h"
# include "Timer.h"
# include "MessageHandle.h"

App::App() 
{
	CoInitialize(NULL);
	this->m_mutex = new std::shared_mutex();
	this->m_fps_control = new Timer(m_fps);
	int result = VideoCapture::enumCaptureToVector(this->m_video_capture_vector);
	if (result != S_OK) {
		// TODO 日志记录
	}
	result = AudioCapture::enumCaptureToVector(this->m_audio_capture_vector);
	this->m_audio_capture = this->m_audio_capture_vector->at(0);
	this->m_audio_capture->startCapture();
	this->initAudioEncoder(this->m_audio_capture->getSampleRate(), 64 * 1000);
	this->m_audio_capture->captureCallback = std::bind(&AudioEncoder::onPcmData, this->m_audio_encoder, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
	this->m_save_file_filter = new SaveFileFilter();
	this->m_get_data_filter = new GetDataFilter();
	this->m_get_data_filter->addFilter(this->m_save_file_filter);
	this->m_audio_encoder->addFilter(this->m_get_data_filter);

	// 网络
	this->m_server = new Client(nullptr, 300);
	this->m_messageHandle = new MessageHandle(this);
	this->m_messageHandle->output = std::bind(&App::log, this, std::placeholders::_1);
	this->m_server->setOnMessageCallbackFunction(std::bind(&App::serverOnMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
	this->m_server->setOnConnectedCallbackFunction(std::bind(&App::serverOnConnected, this, std::placeholders::_1));
	this->m_server->setOnClosedCallbackFunction(std::bind(&App::serverOnClosed, this, std::placeholders::_1));
	this->m_server->start();
	this->runMainThread();
}

int App::runWindow(int argc, char* argv[])
{
	// 创建ui并启动事件循环
	return MainWindow::runUi(argc, argv, &this->m_window, &this->m_ui_state, std::bind(&App::onUiCreateCallback, this));
}

App::~App() {
	this->stopMainThread();
	this->closeWindow();
	delete this->m_server;
	delete this->m_messageHandle;
	delete this->m_remote_window;
	delete this->m_mutex;
	VideoCapture::releaseCaputreFromVector(this->m_video_capture_vector);
	AudioCapture::releaseCaputreFromVector(this->m_audio_capture_vector);
	delete this->m_video_encoder;
	delete this->m_video_decoder;
	delete this->m_audio_encoder;
	delete this->m_save_file_filter;
	delete this->m_get_data_filter;
	delete[] this->m_decode_buffer;
	CoUninitialize();
}

void App::createDecodeBuffer(size_t _size)
{
	this->m_mutex->lock();
	this->m_decode_buffer_size = _size;
	this->m_decode_buffer = new uint8_t[_size];
	this->m_mutex->unlock();
}

void App::onRemoteVideoFrame(uint8_t* _data, size_t _size)
{
	//this->m_mutex->lock();
	this->m_video_decoder->onData(_data, _size, this->m_decode_buffer, this->m_decode_buffer_size);
	//this->m_mutex->unlock();
}

uint8_t* App::getEncodeContext(size_t& _size) const
{
	uint8_t* contextData = nullptr;
	size_t _write_size = 0;
	const uint8_t* data_arr[3] = { nullptr };
	size_t linesize[3] = { 0 };
	if (this->m_video_encoder == nullptr) {
		return nullptr;
	}
	if (this->m_video_encoder != nullptr) {
		data_arr[0] = this->m_video_encoder->getEncodeCodecCtx(&linesize[0]);
		data_arr[1] = this->m_video_encoder->getExtradata(&linesize[1]);
	}
	if (this->m_audio_encoder != nullptr) {
		data_arr[2] = this->m_audio_encoder->getEncodeCodecCtx(&linesize[2]);
	}
	_size = linesize[0] + linesize[1] + linesize[2];
	contextData = new uint8_t[_size];
	for (int i = 0; i < 3; ++i) {
		memcpy_s(contextData + _write_size, linesize[i], data_arr[i], linesize[i]);
		_write_size += linesize[i];
	}
	return contextData;
}

void App::initVideoEncoder(int _width, int _height, int _fps, int _bit_rate)
{
	if (this->m_video_encoder != nullptr) {
		delete this->m_video_encoder;
	}
	this->m_video_encoder = new VideoEncoder(_width, _height, _fps, _bit_rate);
	if (this->m_video_encoder != nullptr) {
		this->m_video_encoder->setFilter(this->m_get_data_filter);
		/*size_t offset;
		uint8_t* ctx = this->getEncodeContext(offset);
		this->createDecodeBuffer(2560 * 1440 * 4);
		VideoDecoder::parse(reinterpret_cast<char*>(ctx), offset);
		this->m_video_decoder = new VideoDecoder(ctx);
		this->m_remote_window = new ShowRemoteWindow();*/
	}
}

void App::initAudioEncoder(int _sample_rate, int _bit_rate)
{
	if (this->m_audio_encoder != nullptr) {
		delete this->m_audio_encoder;
	}

	this->m_audio_encoder = new AudioEncoder(AV_SAMPLE_FMT_S16, _sample_rate, _bit_rate);
	if (this->m_audio_encoder != nullptr) {
		this->m_audio_encoder->addFilter(this->m_get_data_filter);
	}
}

void App::initVideoDecoder(uint8_t* _video_encoder_ctx)
{
	this->m_mutex->lock();
	//delete this->m_remote_window;
	//this->m_remote_window = nullptr;
	delete this->m_video_decoder;
	this->m_video_decoder = nullptr;
	this->m_video_decoder = new VideoDecoder(_video_encoder_ctx);
	this->m_mutex->unlock();
}

void App::onUiCreateCallback()
{
	// ui创建时回调
	this->m_window->onRecordCallback = std::bind(&App::onRecordCallback, this, std::placeholders::_1);
	this->m_window->onRecordStopCallbcak = std::bind(&App::onRecordStopCallbcak, this);
	this->m_window->onScreenCallback = std::bind(&App::onScreenCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
	this->m_window->onScreenStopCallback = std::bind(&App::onScreenStopCallback, this);
	this->m_window->onConnectCallback = std::bind(&App::onConnectCallback, this, std::placeholders::_1);
	this->m_window->onMessageInputCallback = std::bind(&App::onMessageInputCallback, this, std::placeholders::_1);
	this->m_ui_state = true;
	this->log(QString::number(this->m_server->port()));
}

void App::onScreenCallback(int _videoVectorIndex, int* _videoCaptureWidthPtr, int* _videoCaptureHeightPtr)
{
	// 点击捕获屏幕时回调
	this->m_mutex->lock();
	if (this->m_video_capture_vector == nullptr) {
		this->closeWindow();
		return;
	}
	this->m_video_capture = this->m_video_capture_vector->at(_videoVectorIndex);
	(*_videoCaptureWidthPtr) = this->m_video_capture->width();
	(*_videoCaptureHeightPtr) = this->m_video_capture->height();

	if (this->m_video_encoder == nullptr) {
		this->initVideoEncoder(this->m_video_capture->width(), this->m_video_capture->height(), this->m_fps, 2500);
	}

	this->m_mutex->unlock();
}

void App::onScreenStopCallback()
{
	// 停止捕获屏幕时回调
	this->m_mutex->lock();
	this->m_video_capture = nullptr;
	this->m_mutex->unlock();
}

const char* App::onRecordCallback(const char* _saveFileName)
{
	this->m_mutex->lock();
	if (this->m_video_encoder == nullptr) {
		if (this->m_video_capture == nullptr) {
			// TODO 记录
			return "请选择捕获设备";
		}
		this->initVideoEncoder(this->m_video_capture->width(), this->m_video_capture->height(), this->m_fps, 2500 * 1000);
	}
	if (this->m_save_file_filter != nullptr) {
		this->m_save_file_filter->saveFile();
		this->m_save_file_filter->initFileFilter(_saveFileName, this->m_video_encoder->getEncodeCodecCtx(nullptr), this->m_audio_encoder->getEncodeCodecCtx(nullptr));
	}
	this->m_mutex->unlock();
	return nullptr;
}

void App::onRecordStopCallbcak()
{
	// 停止录制回调
	this->m_mutex->lock();
	if (this->m_save_file_filter != nullptr) {
		this->m_save_file_filter->saveFile();
	}
	this->m_mutex->unlock();
}

void App::onConnectCallback(short _port)
{
	this->m_mutex->lock();
	this->m_server->connect(1, "127.0.0.1", _port, true);
	this->m_remote_window = new ShowRemoteWindow();
	this->m_mutex->unlock();
}

void App::onMessageInputCallback(const QString& _message)
{
	if (this->m_server != nullptr) {
		this->m_server->sendMessage(1, std::to_string(CustomHeader::Message), _message.toStdString());
	}
}

bool App::serverOnMessage(int _conv, char* _key, std::string _payload, char* _res_key, char* _response)
{
	this->m_messageHandle->parse(_key, std::move(_payload));
	delete[] _key;
	// delete[] _payload;
	return false;
}

bool App::serverOnConnected(unsigned int conv)
{
	this->log("connect: " + QString::number(conv));
	size_t size = 0;
	uint8_t* encoder_ctx = this->getEncodeContext(size);
	if (encoder_ctx != nullptr) {
		this->m_messageHandle->sendEncodeContext(this->m_server, 1, encoder_ctx, size);
	}
	delete[] encoder_ctx;
	this->m_connect_conv = conv;
	return false;
}

bool App::serverOnClosed(unsigned int conv)
{
	this->log("close: " + QString::number(conv));
	this->m_connect_conv = 0;
	return false;
}

void App::closeWindow()
{
	if (this->m_ui_state) {
		this->m_window->close();
	}
	delete this->m_window;
}

void App::runMainThread()
{
	this->stopMainThread();
	this->m_main_thread = new std::thread(&App::mainFunc, this);
}

void App::stopMainThread()
{
	if (this->m_main_thread != nullptr) {
		this->m_main_thread_alive = false;
		this->m_main_thread->join();
		delete this->m_main_thread;
		this->m_main_thread = nullptr;
	}
}

void App::mainFunc()
{
	// 主线程
	this->m_main_thread_alive = true;
	uint8_t* videoRenderBuffer = nullptr;
	size_t videoCaptureBufferSize = 0;
	AVFrame* frame = nullptr;
	uint8_t* decoderBuffer = nullptr;
	bool changeVideoCaptureBufferSize = true;
	double f = 0.0f;
	while (this->m_main_thread_alive) {
		this->m_fps_control->mark();
		this->m_mutex->lock_shared();

		// 有捕获的屏幕
		if (this->m_video_capture != nullptr) {
			this->m_video_capture->capture();
			if (changeVideoCaptureBufferSize) {
				videoCaptureBufferSize = this->m_video_capture->width() * this->m_video_capture->height() * 4;
				delete[] videoRenderBuffer;
				videoRenderBuffer = new uint8_t[videoCaptureBufferSize];
				delete[] decoderBuffer;
				decoderBuffer = new uint8_t[videoCaptureBufferSize];
				changeVideoCaptureBufferSize = false;
			}
		}
		else {
			changeVideoCaptureBufferSize = true;
		}

		// ui存在
		if (this->m_ui_state) {
			if (this->m_video_capture != nullptr) {
				this->m_window->videoCaptureToShowWindow(this->m_video_capture->getDevice(), this->m_video_capture->getContext(), this->m_video_capture->getImageTexture());
			}
			this->m_window->frame();
		}

		if (this->m_video_encoder != nullptr) {
			this->m_window->copyFromRenderTarget(videoRenderBuffer, videoCaptureBufferSize);
			this->m_video_encoder->onBGRAData(videoRenderBuffer);
			if (this->m_server != nullptr && this->m_connect_conv != 0) {
				this->m_messageHandle->sendVideoData(this->m_server, this->m_connect_conv, this->m_get_data_filter->getVideoData(), this->m_get_data_filter->getVideoDataSize());
			}
		}

		if (this->m_video_decoder != nullptr) {
			// this->m_messageHandle->parse(std::to_string(2).data(), std::string(this->m_get_data_filter->getVideoData(), this->m_get_data_filter->getVideoData() + this->m_get_data_filter->getVideoDataSize()));
			/*this->m_video_decoder->onData(this->m_get_data_filter->getVideoData(), this->m_get_data_filter->getVideoDataSize(), decoderBuffer, videoCaptureBufferSize);
			this->m_remote_window->copyToRemoteTexture(decoderBuffer, 2560 * 4);
			this->m_remote_window->rend();*/
			this->m_remote_window->copyToRemoteTexture(this->m_decode_buffer, 2560 * 4);
			this->m_remote_window->rend();
		}

		this->m_mutex->unlock_shared();
		f = this->m_fps_control->nextFps();
	}
	if (this->m_save_file_filter != nullptr) {
		this->m_save_file_filter->saveFile();
	}
	delete[] videoRenderBuffer;
	delete[] decoderBuffer;
}

void App::log(const QString& _message)
{
	if (this->m_ui_state) {
		this->m_window->log(_message);
	}
}
